package utils

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
)

// WechatSession 微信登录返回的session信息
type WechatSession struct {
	OpenID     string `json:"openid"`
	SessionKey string `json:"session_key"`
	UnionID    string `json:"unionid"`
	ErrCode    int    `json:"errcode"`
	ErrMsg     string `json:"errmsg"`
}

// WechatPhoneInfo 微信手机号信息
type WechatPhoneInfo struct {
	PhoneNumber     string `json:"phoneNumber"`
	PurePhoneNumber string `json:"purePhoneNumber"`
	CountryCode     string `json:"countryCode"`
	Watermark       struct {
		Timestamp int64  `json:"timestamp"`
		AppID     string `json:"appid"`
	} `json:"watermark"`
}

// Code2Session 通过code获取session信息
func Code2Session(appID, appSecret, code string) (*WechatSession, error) {
	url := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
		appID, appSecret, code)

	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var session WechatSession
	if err := json.Unmarshal(body, &session); err != nil {
		return nil, err
	}

	if session.ErrCode != 0 {
		return nil, errors.New(session.ErrMsg)
	}

	return &session, nil
}

// DecryptWechatData 解密微信数据
func DecryptWechatData(encryptedData, iv, sessionKey, appID string) (*WechatPhoneInfo, error) {
	aesKey, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return nil, err
	}

	aesIV, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return nil, err
	}

	cipherText, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return nil, err
	}

	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, err
	}

	mode := cipher.NewCBCDecrypter(block, aesIV)
	plainText := make([]byte, len(cipherText))
	mode.CryptBlocks(plainText, cipherText)

	// 去除padding
	plainText = pkcs7UnPadding(plainText)

	var phoneInfo WechatPhoneInfo
	if err := json.Unmarshal(plainText, &phoneInfo); err != nil {
		return nil, err
	}

	// 验证appid
	if phoneInfo.Watermark.AppID != appID {
		return nil, errors.New("appid not match")
	}

	return &phoneInfo, nil
}

func pkcs7UnPadding(data []byte) []byte {
	length := len(data)
	if length == 0 {
		return data
	}
	unPadding := int(data[length-1])
	return data[:(length - unPadding)]
}
